package information;

import java.io.File;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
//import com.sun.xml.internal.stream.writers.XMLWriter;

/**
 * Reads data from specified files and transforms them into classes used by application.
 * @author Christian
 *
 */
public class FileIO {
	//file names for data files to load into application and save from application
	private static final String XML_USERS = "users.xml";
	private static final String XML_USERS_SAVE = "users_save.xml";
	private static final String XML_JOBS = "jobs.xml";
	private static final String XML_JOBS_SAVE = "jobs_save.xml";
	private String TF;
	//these two sets are used to locate user via email within a job
	private Set<User> myVolunteers;
	private Set<User> myParkmanagers;
	private Set<User> myTempSet; 
	
	
	public FileIO() {
		myVolunteers = new HashSet<User>();
		myParkmanagers = new HashSet<User>();
	}

	/**
	 * Helper method used to get a user from class field from an email and usertype.
	 * To limit data file clutter, the email and usertype are the only data stored in file.
	 * @param theUsertype
	 * @param theEmail
	 * @return The user associated with the email passed in
	 */
	//@requires theUsertype != null, theEmail != null
	public User getUser(final Usertype theUsertype, final String theEmail) {
		User thisUser = null;
		Set<User> userSet;
		//figure out which set of users to iterate through for this user
		if (theUsertype == Usertype.PARKMANAGER) {
			userSet = myParkmanagers;
		} else {
			userSet = myVolunteers;
		}
		//find the user via email
		for (User user : userSet) {
			if (user.getEmail().equals(theEmail)) {
				thisUser = user;
				break;
			}
		}
		return thisUser; 
	}
	
	/**
	 * Creates sets of archived and current Job objects from the backing store (xml file).
	 * Had to hardcode the file location to C:// because it we didn't have time
	 * to figure out how to uncompress xml file from jar file.
	 * @param theCurrentJobs
	 * @param theArchivedJobs
	 */
	//@requires that the source file is valid and in a location that is in the same folder as this file
	//@requires that the source file is formatted correctly!!
	//@ensures that the sets of Jobs are created from the file
	public void createJobs(Set<Job> theCurrentJobs, Set<Job> theArchivedJobs) {
		try {
//			URL url = getClass().getResource("/"+XML_JOBS); //doesn't work with JAR
//			File fXmlFile = new File(url.toURI()); //doesn't work with JAR
			File fXmlFile = new File("C://jobs.xml");
			//this is xml specific for building documents from xml
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();
			
			NodeList jobsList = doc.getElementsByTagName("Job");
			int totalJobs = jobsList.getLength();
			
			for (int job = 0; job < totalJobs; job++) {
				//initialize variables
				User manager = null;
				List<String> levelDetails = new ArrayList<String>();
				List<Integer> levelNumbers = new ArrayList<Integer>();
				List<Date> dates = new ArrayList<Date>();
				String description = null;
				String datesString = null;
				boolean holiday = false;
				boolean weekday = false;
				Park selPark = null;
				
				Node jobNode = jobsList.item(job);
				Element jobElement = (Element) jobNode;
				
				//fetching description
				description = getTagValue("description", jobElement);
				//fetching dates
				datesString = getTagValue("dates", jobElement);
				String[] bothDates = datesString.split("-");
				for (int i = 0; i < bothDates.length; i++) {
					String[] splitDate = bothDates[i].split("/");
					int year = Integer.parseInt(splitDate[0]);
					int month = Integer.parseInt(splitDate[1]);
					int day = Integer.parseInt(splitDate[2]);
					int hour = Integer.parseInt(splitDate[3]);
					int minute = Integer.parseInt(splitDate[4]);
					dates.add(new Date(year, month, day, hour, minute));
				}
				//fetching park manager
				manager = getUser(Usertype.PARKMANAGER, getTagValue("parkManagerEmail", jobElement));
				//fetching level details
				NodeList levels = jobElement.getElementsByTagName("levelDetails");
				Node levelNode = levels.item(0);
				Element lvlElement = (Element) levelNode;
				levelDetails.add(getTagValue("low", lvlElement));
				levelDetails.add(getTagValue("med", lvlElement));
				levelDetails.add(getTagValue("high", lvlElement));
				//fetching level numbers
				NodeList levelNums = jobElement.getElementsByTagName("levelNumbers");
				Node levelNumNode = levelNums.item(0);
				Element lvlNumElement = (Element) levelNumNode;
				levelNumbers.add(Integer.parseInt(getTagValue("low", lvlNumElement)));
				levelNumbers.add(Integer.parseInt(getTagValue("med", lvlNumElement)));
				levelNumbers.add(Integer.parseInt(getTagValue("high", lvlNumElement)));
				//fetching holiday
				holiday = Boolean.parseBoolean(getTagValue("holiday", jobElement));
				//fetching weekday
				weekday = Boolean.parseBoolean(getTagValue("weekday", jobElement));
				//fetching park
				NodeList parkNl = jobElement.getElementsByTagName("Park");
				Node parkNode = parkNl.item(0);
				Element parkElement = (Element) parkNode;
				String pName = getTagValue("name", parkElement);
				String pLocation = getTagValue("name", parkElement);
				String pRegion = getTagValue("region", parkElement);
				String pDistrict = getTagValue("district", parkElement);
				String pBus = getTagValue("bus", parkElement);
				selPark = new Park(pName, pRegion, pDistrict, pLocation, pBus);
				
				//have to create the job before adding volunteers...
				Job placeholder = new Job(manager, levelDetails, levelNumbers, dates.get(0), dates.get(1), description, holiday, weekday, selPark);
				
				//fetching volunteers
				NodeList vols = jobElement.getElementsByTagName("volunteers");
				Node volNode = vols.item(0);
				Element volElement = (Element) volNode;
				NodeList vs = volElement.getElementsByTagName("volunteer");
				for (int i = 0; i < vs.getLength(); i++) {
					Node vn = vs.item(i);
					Element v = (Element) vn;
					String volEmail = getTagValue("email", v);
					User volunteer = getUser(Usertype.VOLUNTEER, volEmail);
					int level = Integer.parseInt(getTagValue("level", v));
					placeholder.addVolunteer(volunteer, level);						
				}
				
				//add the job to either set
				if (dates.get(1).isFuture()) {
					theCurrentJobs.add(placeholder);
				} else {
					theArchivedJobs.add(placeholder);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Creates a set of User objects from the backing store (xml file).
	 * Had to hardcode the file location to C:// because it we didn't have time
	 * to figure out how to uncompress xml file from jar file.
	 * @return The set of User objects
	 */
	//@requires that the source file is valid and in a location that is in the same folder as this file
	//@requires that the source file is formatted correctly!!
	//@ensures that the sets of Users are created from the file
	public Set<User> createUsers() {
		Set<User> users = new HashSet<User>();
		try {
//			URL url = getClass().getResource("/"+XML_USERS);
//			File fXmlFile = new File(url.toURI());
			File fXmlFile = new File("C://users.xml");
			
			//xml specific stuff
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();
			
			NodeList usersList = doc.getElementsByTagName("User");
			int totalUsers = usersList.getLength();
			
			for (int user = 0; user < totalUsers; user++) {
				String tempName;
				String tempEmail;
				String tempPhone;
				Set<Park> tempParks = null;
				boolean tempBB;
				Usertype tempUsertype;
				
				Node userNode = usersList.item(user);
				Element userElement = (Element) userNode;
				tempName = getTagValue("myName", userElement);
			    tempEmail = getTagValue("myEmail", userElement);
			    tempPhone = getTagValue("myPhone", userElement);	
			    tempUsertype = Usertype.valueOf(getTagValue("myUsertype", userElement));
				tempBB = Boolean.parseBoolean(getTagValue("myBlackballStatus", userElement));
				//if user is park manager => create parks
				if (Usertype.valueOf(getTagValue("myUsertype", userElement)) == Usertype.PARKMANAGER) {
					tempParks = new HashSet<Park>();
					NodeList parks = userElement.getElementsByTagName("myParks");
					Node parkNode = parks.item(0);
					Element parkElement = (Element) parkNode;
					NodeList pl = parkElement.getElementsByTagName("Park");
					for (int i = 0; i < pl.getLength(); i++) {
						Node p = pl.item(i);
						Element pe = (Element) p;
						String pName = getTagValue("name", pe);
						String pRegion = getTagValue("region", pe);
						String pDistrict = getTagValue("district", pe);
						String pLocation = getTagValue("location", pe);
						String pBus = getTagValue("bus", pe);
						tempParks.add(new Park(pName, pRegion, pDistrict, pLocation, pBus));
					}
				}
				
				User placeholder = new User(tempUsertype, tempParks, tempName, tempEmail, tempPhone, tempBB);
				users.add((placeholder));
				
				//these are to add users to jobs so we don't have to store entire user data in jobs; only email and name - same thing checked for login
				if (placeholder.getUsertype() == Usertype.PARKMANAGER) {
					myParkmanagers.add(placeholder);
				} else if (placeholder.getUsertype() == Usertype.VOLUNTEER) {
					myVolunteers.add(placeholder);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return users;
	}
	
	/**
	 * @author Barry
	 * @param sTag
	 * @param eElement
	 * @return The tag element
	 */
	private static String getTagValue(String sTag, Element eElement) {
		NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes();
	 
	        Node nValue = (Node) nlList.item(0);
	 
		return nValue.getNodeValue();
	}
	
	/**
	 * Incomplete method for saving jobs to file.
	 * Saves sets of Job objects to xml file.
	 * @param theCurrentJobs
	 * @param theArchivedJobs
	 */
	//@requires that the sets of jobs passed in != null
	public void saveJobs(final Set<Job> theCurrentJobs, final Set<Job> theArchivedJobs) {
		List<Job> totalJobs = new ArrayList<Job>(theCurrentJobs);
		totalJobs.addAll(theArchivedJobs);
		try {
			URL url = getClass().getResource("/"+XML_JOBS_SAVE);
			//create document
			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
			//create xml tree
			Element root = doc.createElement("Jobs");
			doc.appendChild(root);
			
			for (Job job : totalJobs) {
				
				//build job element
				Element jobElement = doc.createElement("Job");
				//build parkmanager element
				Element pmEmailElement = doc.createElement("parkManagerEmail");
				Text pmEmailText = doc.createTextNode(job.getManager().getEmail());
				pmEmailElement.appendChild(pmEmailText);
				jobElement.appendChild(pmEmailElement);
				//build volunteers
				Element volsElement = doc.createElement("volunteers");
				
				myTempSet = job.getVolunteers();
				//System.out.println(myTempSet.size());
				for (User user : myTempSet) {
					Element volElement = doc.createElement("volunteer");
					Element emElement = doc.createElement("email");
					Text nameText = doc.createTextNode(user.getEmail());
					emElement.appendChild(nameText);
					Element levelElement = doc.createElement("level");
					Text levelText = doc.createTextNode(""+ job.getVolunteerLevel(user));
					levelElement.appendChild(levelText);
					volElement.appendChild(emElement);
					volElement.appendChild(levelElement);
					volsElement.appendChild(volElement);	
					
				}
				jobElement.appendChild(volsElement);
				//build level details
				Element detElement = doc.createElement("levelDetails");
				Element level1Element = doc.createElement("low");
				Text lvl1Text = doc.createTextNode(job.getLightDetials());
				level1Element.appendChild(lvl1Text);
				Element level2Element = doc.createElement("med");
				Text lvl2Text = doc.createTextNode(job.getMediumDetials());
				level2Element.appendChild(lvl2Text);
				Element level3Element = doc.createElement("high");
				Text lvl3Text = doc.createTextNode(job.getHeavyDetails());
				level3Element.appendChild(lvl3Text);
				detElement.appendChild(level1Element);
				detElement.appendChild(level2Element);
				detElement.appendChild(level3Element);
				jobElement.appendChild(detElement);
				//build level numbers
				Element det2Element = doc.createElement("levelNumbers");
				Element level1Element2 = doc.createElement("low");
				Text lvl1Text2 = doc.createTextNode(Integer.toString(job.getLightNumber()));
				level1Element2.appendChild(lvl1Text2);
				Element level2Element2 = doc.createElement("med");
				Text lvl2Text2 = doc.createTextNode(Integer.toString(job.getMediumNumber()));
				level2Element2.appendChild(lvl2Text2);
				Element level3Element2 = doc.createElement("high");
				Text lvl3Text2 = doc.createTextNode(Integer.toString(job.getMediumNumber()));
				level3Element2.appendChild(lvl3Text2);
				det2Element.appendChild(level1Element2);
				det2Element.appendChild(level2Element2);
				det2Element.appendChild(level3Element2);
				jobElement.appendChild(det2Element);
				//build dates
				Element dateElement = doc.createElement("dates");
				Text dateText = doc.createTextNode(job.getStartDate().toXmlString()+ "-"+ job.getEndDate().toXmlString());
				dateElement.appendChild(dateText );
				jobElement.appendChild(dateElement);
				//build description
				Element decElement = doc.createElement("description");
				Text decText = doc.createTextNode(job.getDescription());
				decElement.appendChild(decText);
				jobElement.appendChild( decElement);
				//build holiday
				Element holElement = doc.createElement("holiday");
				if (job.isHoliday()){TF = "false";}
				else {TF = "true";}
				Text holText = doc.createTextNode(TF);
				holElement.appendChild(holText);
				jobElement.appendChild( holElement);
				//build weekday
				Element wekElement = doc.createElement("weekday");
				if (job.isWeekday()){TF = "false";}
				else {TF = "true";}
				Text wekText = doc.createTextNode(TF);
				wekElement.appendChild(wekText);
				jobElement.appendChild( wekElement);
				//build parks					
					Element parkElement = doc.createElement("Park");
					Park tpark =job.getPark();
						Element pnElement = doc.createElement("name");
						Text pnText = doc.createTextNode(tpark.getMyName());
						Element plElement = doc.createElement("location");
						Text plText = doc.createTextNode(tpark.getMyName());
						Element prElement = doc.createElement("region");
						Text prText = doc.createTextNode(tpark.getMyRegion());
						Element pdElement = doc.createElement("district");
						Text pdText = doc.createTextNode(tpark.getMyDistrict());
						Element pbElement = doc.createElement("bus");
						Text pbText = doc.createTextNode(tpark.getMyBus());
						pnElement.appendChild(pnText);
						plElement.appendChild(plText);
						prElement.appendChild(prText);
						pbElement.appendChild(pdText);
						pdElement.appendChild(pbText);
						parkElement.appendChild(pnElement);
						parkElement.appendChild(plElement);
						parkElement.appendChild(prElement);
						parkElement.appendChild(pdElement);
						parkElement.appendChild(pbElement);
				  jobElement.appendChild(parkElement);	
				
				//putting it all together
				
				
				root.appendChild(jobElement);
			}
			//output xml
			Transformer xformer = TransformerFactory.newInstance().newTransformer();
			xformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			xformer.setOutputProperty(OutputKeys.INDENT, "yes");
            //create string from xml tree
			StringWriter sw = new StringWriter();
            StreamResult sResult = new StreamResult("C:\\jobs.xml");
            DOMSource source = new DOMSource(doc);
            xformer.transform(source, sResult);
            String xmlString = sw.toString();
            //print xml
            //System.out.println("Here's the jobs xml:\n\n" + xmlString);
            
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Method for saving the set of Users to a data file.
	 * This method saves to a file that is not in the jar file.
	 * We couldn't figure out why it couldn't save to project, and then when we jarred it, we found out
	 * that there is a compression issue when saving.
	 * @author Christian
	 * @author Barry
	 * @param theUsers
	 */
	//@requires theUsers != null
	public void saveUsers(final Set<User> theUsers) {
		try {
			URL url = getClass().getResource("/"+XML_USERS_SAVE);
			File fXmlFile = new File(url.toURI());
			//create document
			DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			Document doc = docBuilder.newDocument();
			//create xml tree
			Element root = doc.createElement("Users");
			doc.appendChild(root);
			
			for (User user : theUsers) {
				Element userElement = doc.createElement("User");

				//build usertype child and append it to user
				Element usertypeElement = doc.createElement("myUsertype");
				Text typeText = doc.createTextNode(user.getUsertype().toString());
				usertypeElement.appendChild(typeText);
				userElement.appendChild(usertypeElement);
				//park node
				Element parksElement = doc.createElement("myParks");
				if (user.getUsertype() == Usertype.PARKMANAGER) {					
					Set<Park> parks = user.getParks();
					Iterator<Park> pit = parks.iterator();
					while (pit.hasNext()) {
						Element parkElement = doc.createElement("Park");
						Park park = pit.next();
						Element pnElement = doc.createElement("name");
						Text pnText = doc.createTextNode(park.getMyName());
						Element plElement = doc.createElement("location");
						Text plText = doc.createTextNode(park.getMyName());
						Element prElement = doc.createElement("region");
						Text prText = doc.createTextNode(park.getMyRegion());
						Element pdElement = doc.createElement("district");
						Text pdText = doc.createTextNode(park.getMyDistrict());
						Element pbElement = doc.createElement("bus");
						Text pbText = doc.createTextNode(park.getMyBus());
						pnElement.appendChild(pnText);
						plElement.appendChild(plText);
						prElement.appendChild(prText);
						pbElement.appendChild(pdText);
						pdElement.appendChild(pbText);
						parkElement.appendChild(pnElement);
						parkElement.appendChild(plElement);
						parkElement.appendChild(prElement);
						parkElement.appendChild(pdElement);
						parkElement.appendChild(pbElement);
						parksElement.appendChild(parkElement);						
					}
				} else {
					Text parkText = doc.createTextNode("null");
					parksElement.appendChild(parkText);					
				}
				userElement.appendChild(parksElement);
				//build name child and append it to user
				Element nameElement = doc.createElement("myName");
				Text nameText = doc.createTextNode(user.getName());
				nameElement.appendChild(nameText);
				userElement.appendChild(nameElement);
				//build email child and append it to user
				Element emailElement = doc.createElement("myEmail");
				Text emailText = doc.createTextNode(user.getEmail());
				emailElement.appendChild(emailText);
				userElement.appendChild(emailElement);
				//phone element
				Element phoneElement = doc.createElement("myPhone");
				Text phoneText = doc.createTextNode(user.getPhone());
				phoneElement.appendChild(phoneText);
				userElement.appendChild(phoneElement);
				//bb element
				Element bbElement = doc.createElement("myBlackballStatus");
				Text bbText = doc.createTextNode(new Boolean(user.isBlackballed()).toString());
				bbElement.appendChild(bbText);
				userElement.appendChild(bbElement);		
				root.appendChild(userElement);
			}
				TransformerFactory transformerFactory = TransformerFactory.newInstance();
				Transformer transformer = transformerFactory.newTransformer();
				DOMSource source = new DOMSource(doc);
				StreamResult result = new StreamResult("C:\\users.xml");
				
				transformer.transform(source, result);
//          //this shit should write it to file, but fails, "C:\\file.xml"
//			DOMSource source = new DOMSource(doc);
//			File file = new File(url.toURI());
//			Result result = new StreamResult(file);
////			StreamResult result = new StreamResult(new File(url.toURI()));
//			Transformer xformer = TransformerFactory.newInstance().newTransformer();
//			xformer.transform(source, result);
			
			//output xml
			/*
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
//			xformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			transformer.setOutputProperty(OutputKeys.METHOD, "xml");
			transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
//            //create string from xml tree
			StringWriter sw = new StringWriter();
            StreamResult sResult = new StreamResult(sw);
            //trying to save to file
			File file = new File(url.toURI());
			FileWriter fw = new FileWriter(file);
			StreamResult result = new StreamResult(fw);
//			Writer wr = new Writer(fw);
            transformer.transform(new DOMSource(doc), sResult); //writes to string for output to console
//			transformer.transform(new DOMSource(doc), new XMLWriter(fw).toXMLResult()); //doesn't work - in org.eclipse.... which i guess is no longer supported
            transformer.transform(new DOMSource(doc), result); //supposed to write to file, but doesn't work
            String xmlString = sw.toString();
//            //print xml
            System.out.println("Here's the users xml:\n\n" + xmlString);
//            System.out.println(url.toURI().toString());
            */
            
			

		} catch (Exception e) {
		e.printStackTrace();
		}
		//catch (ParserConfigurationException pce) {
		//	pce.printStackTrace();
		 // } catch (TransformerException tfe) {
			//tfe.printStackTrace();
		 // }	
	}
	
//	public void saveParks(final Set<Park> theParks) {
//		
//	}
	
//	public void saveInvariants(final Set<Invariant> theInvariants) {
//		
//	}
}
